var FaviconCollective = {

	QueryInterface: function(aIID)
	{
		if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
		aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
		aIID.equals(Components.interfaces.nsISupports))
		     return this;
		throw Components.results.NS_NOINTERFACE;
	},

	onLocationChange: function(aProgress, aRequest, aURI)
	{
		try {
			var faviconImageDataLen = {};
			var faviconImageMimeType = {};
			var faviconService = Components.classes["@mozilla.org/browser/favicon-service;1"].getService(Components.interfaces.nsIFaviconService);
                        var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
			var faviconURI = faviconService.getFaviconForPage(aURI);
			faviconService.setAndLoadFaviconForPage(aURI, faviconURI, false);

//                	Components.utils.reportError("polvi faviconURI: " + faviconURI.spec + " aURI: " + aURI.spec);
			var faviconImageData = faviconService.getFaviconData(faviconURI, faviconImageMimeType, faviconImageDataLen);
			var faviconMD5 = this.md5_hasher(faviconImageData)
			var faviconBytes = String.fromCharCode.apply(null, faviconImageData)
			this.sendFavicon(faviconBytes,faviconImageMimeType.value, faviconURI.spec, faviconMD5);
			return;
		} catch (e) {
                	Components.utils.reportError("favicon exception");
		}

		return 0;
	},

	onProgressChange: function() {return 0;},
	onStateChange: function() {return 0;},
	onStatusChange: function() {return 0;},
	onSecurityChange: function() {return 0;},
	onLinkIconAvailable: function() {return 0;},

	md5_hasher: function(data)
	{
		var ch = Components.classes["@mozilla.org/security/hash;1"]
			.createInstance(Components.interfaces.nsICryptoHash);
		ch.init(ch.MD5);
		ch.update(data, data.length);
		var hash = ch.finish(false);

		// return the two-digit hexadecimal code for a byte
		function toHexString(charCode)
		{
			return ("0" + charCode.toString(16)).slice(-2);
		}

		// convert the binary hash data to a hex string.
		return [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
	},

	getClientID: function () 
	{
		var clientID = this.prefs.getCharPref("extensions.favicon.client-id");

		if (clientID == '') {
			var uuidGenerator = Components.classes["@mozilla.org/uuid-generator;1"]
				.getService(Components.interfaces.nsIUUIDGenerator);
			var uuid = uuidGenerator.generateUUID();
			clientID = uuid.toString();
			this.prefs.setCharPref("extensions.favicon.client-id", clientID);

		}
		return clientID;
	},

	startup: function()
	{
//                Components.utils.reportError("startup fired");
		this.prefs = Components.classes["@mozilla.org/preferences-service;1"]
			.getService(Components.interfaces.nsIPrefService);
		this.clientID = this.getClientID();
		gBrowser.addProgressListener(this, Components.interfaces.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
	},

	sendFavicon: function(faviconImage, faviconMimeType, faviconURI, faviconMD5) 
	{
		var httpRequest = null;

		function infoReceived() { /* we really don't care */ }

		// if we shouldn't be sending https, bail now
		if (!this.prefs.getBoolPref("extensions.favicon.https.enabled") &&
			faviconURI.substring(0,5) == "https")
			return;
		
		http = new XMLHttpRequest();
		
		// send $_FILE with an XHR
		// http://news.hping.org/comp.lang.javascript.archive/9693.html
		var boundaryString = '--------------send-favicon-AaB03x'; 
		var boundary = '--' + boundaryString;
		var requestBody = [];

		// faviconURI gets automatically basename'd here
		requestBody = [
			boundary,
			'Content-Disposition: form-data; name="faviconMD5"',
			'',
			faviconMD5,
			boundary,
			'Content-Disposition: form-data; name="favicon"; filename="' + faviconURI + '\"',
			'Content-Type: ' + faviconMimeType,
			'',
			faviconImage
		];

		if (this.prefs.getBoolPref("extensions.favicon.uri.enabled")) {
			requestBody = requestBody.concat([
				boundary,
				'Content-Disposition: form-data; name="faviconURI"',
				'',
				faviconURI
			]);
		}
		if (this.prefs.getBoolPref("extensions.favicon.client-id.enabled")) {
			requestBody = requestBody.concat([
				boundary,
				'Content-Disposition: form-data; name="clientID"',
				'',
				this.clientID
			]);
		}


		requestBody = requestBody.concat([boundary]);
		requestBody = requestBody.join('\r\n');

		http.open("POST", this.prefs.getCharPref("extensions.favicon.upload-uri"), true);
		http.setRequestHeader('Content-Type','multipart/form-data;  boundary=' + boundaryString); 
		http.onload = infoReceived;

		// undocumented API
		// http://mxr.mozilla.org/mozilla/source/content/base/public/nsIXMLHttpRequest.idl#248
		http.sendAsBinary(requestBody);
//		Components.utils.reportError("post fired");
	}
}

// Install load handler
window.addEventListener("load", function(e) { FaviconCollective.startup(); } , false);
